//
//  DPUnitTest.h
//  DPUnitTest
//
//  Created by Ofri Wolfus on 06/10/06.
//  Copyright 2006 Ofri Wolfus. All rights reserved.
//

#import <Cocoa/Cocoa.h>


/*
 * By default, tests are executed in the main() function, and therefor
 * a version of it is implemented in the framework.
 * If you need to do some stuff in the main() function, you can define DPUT_NO_INIT
 * and recompile the framework. Doing that will cause tests to be executed *BEFORE*
 * the main function, when the framework is being initialized.
 * Don't attempt to enable this behavior unless you really have to, as it may (and probably will) not work correctly.
 */
//#define DPUT_NO_INIT

/*!
 * @abstract A base class for unit tests.
 * @discussion In order to use this unit test framework, you must subclass this class (or DPSpeedTest, see below).
 * Any instance method implemented by a subclass, and that its name begins with "test", will automatically be called at runtime.
 * If the return type of a test method is <code>BOOL</code>, it's assumed to be the result of the test (failure/success).
 * In case of a failure, either an exception will be raised, or it'll be logged (see the docs for -throwsForFaildTests).
 */
@interface DPUnitTest : NSObject {
	@private
	NSString *_failReason;
}

/*!
 * @abstract Returns whether an exception should be raised for failed tests defined in a specific subclass.
 * @discussion If the result is <code>YES</code>, an exception will be raised upon a failure.
 * This is useful in case you build with debug symbols, and want to know where exactly your test failed.
 * When using the return value of a test to indicate success/failure, an exception will be raised after the method returns.
 * The default implementation returns <code>YES</code>, but inidividual subclasses should override this to fit their needs.
 */
- (BOOL)throwsForFaildTests;

/*!
 * @abstract Returns whether a message should be logged after a successful test.
 * @discussion The default implementation returns <code>YES</code>, but individual subclasses should override this as needed.
 * Logging of a successful test also contains the avarage time it took to complete the test.
 */
- (BOOL)logsSuccessfulTests;


/*!
 * @abstract This method is used to return the reason a method failed to the framework.
 * @discussion If you use the return value of your test to indicate its completion state,
 * you may use this method to provide more info in case of failure.
 * Generally, you won't need to use this method and you'll use the DPUTAssert() and DPAssert() macros (see below).
 */
- (void)setFailReason:(NSString *)format, ...;

@end


@interface DPSpeedTest : DPUnitTest {
}

- (unsigned)numberOfIterations;
- (BOOL)verifiesAllTests;
- (BOOL)verifyResult:(id)result ofTest:(SEL)testName;

@end

#if !defined(DPAssert)
/*
 * The usage of this macro is the same as with NSAssert, exept you don't need DPAssert1, DPAssert2, etc.
 * This macro accepts any number of arguments passed to the description.
 */
#define DPAssert(condition, desc...)\
do {\
	if (!(condition)) {\
		[[NSAssertionHandler currentHandler] handleFailureInMethod:_cmd\
															object:self\
															  file:[NSString stringWithCString:__FILE__] \
														lineNumber:__LINE__\
													   description:desc];\
	}\
} while(0)
#endif

#if !defined (DPUTAssert)
#define DPUTAssert(condition, desc...) \
do {\
	if (!(condition)) { \
		[self setFailReason:@"%s:%d - %@", __FILE__, __LINE__, [NSString stringWithFormat:desc]]; \
		if ([self throwsForFaildTests]) \
			DPAssert(condition, desc); \
	} \
} while (0)
#endif

#if !defined (DPTestAssert)
#define DPTestAssert(condition, desc...) \
do {\
	if (!(condition)) { \
		DPUTAssert(condition, desc); \
		return NO; \
	} \
} while (0)
#endif
